package com.flca.frw.zk.helper;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listhead;
import org.zkoss.zul.Listheader;
import org.zkoss.zul.Listitem;

import com.flca.frw.beanutils.MethodCacheData;
import com.flca.frw.beanutils.MethodHelper;
import com.flca.frw.util.TransformUtils;
import com.flca.frw.zk.util.Alert;

public class ListboxHelper {

	private static TransformUtils tu = TransformUtils.getInstance();

	public static final String ITEM_KEY = "itemkey";
	
	public static final String HDR_FIRST_STRING = "%s";
	
	public void fillListbox(Listbox aListbox, Collection<?> aItems)
	{
		fillListbox(aListbox, aItems, null, null);
	}

	public void fillListbox(Listbox aListbox, Collection<?> aItems, String aPropertyName) 
	{
		fillListbox(aListbox, aItems, aPropertyName, null);
	}

	/**
	 * This will fill the given listbox with the given data.
	 * The labelnames and propertnames are optional. If both are null, the listbox will have 1 colums with the label "Value" and the data is filled with the toString() from the corr. item. 
	 * The propertynames and labelnames is a comma seperated list of names.
	 * If  the labelnnames is null, the propertynames will be used the column label
	 * The labelnames may also have a with params seperated by a colon
	 * examples:
	 * fillListbox(listbox1, items);  this is identical to fillListbox*listbox1, items, null, null);
	 * fillListbox(listbox1, items, "name") 
	 * fillListbox(listbox1, items, "name", "Naam")
	 * fillListbox(listbox1, items, "name,dob", "Naam:70%, Geboortedatum:30%")
	 * fillListbox(listbox1, items, "%s") or
	 * fillListbox(listbox1, items, ListboxHelper.HDR_FIRST_STRING) The first String column will be used
	 * @param aListbox
	 */
	@SuppressWarnings("unchecked")
	public void fillListbox(Listbox aListbox, Collection<?> aItems,  String aPropertyNames, String aLabelnames) 
	{
		if (aListbox != null && aItems != null && aItems.size() > 0) {
			aListbox.getItems().clear();
			
			try {
				String propNames = makeListheads(aListbox, aItems, aPropertyNames, aLabelnames);

				List<Method> getters = makeGetters(aItems.iterator().next(), propNames);

				for (Object item : aItems) {
					aListbox.getItems().add(makeListitem(item, getters));
				}
			} catch (Exception e) {
				Alert.show("error in fillListbox: " + e);
			}
		} else {
			if (aListbox == null) {
				Alert.show("fillListbox called with bad params");
			}
		}
	}

	@SuppressWarnings("unchecked")
	private String makeListheads(Listbox aListbox, Collection<?> aItems, String aPropertyNames, String aLabelNames) 
	{
		String result = null;
		
		Collection<?> heads = aListbox.getHeads();
		Listhead h = null;
		if (heads != null && heads.size() > 0) {
			h = (Listhead) heads.iterator().next();
			h.getChildren().clear();
			
			if (emptyPropertyName(aPropertyNames)) {
				Listheader hdr = new Listheader("Value");
				hdr.setWidth("100px");
				h.getChildren().add(hdr);
				result = null;
			} else {
				result = aPropertyNames;
				if (aPropertyNames.equals(HDR_FIRST_STRING)) {
					result = getFirstStringProperty(aItems);
				} 
				
				String labels[] = StringUtils.split(result, ',');
				for (String lbl : labels) {
					Listheader hdr = new Listheader(lbl);
					hdr.setWidth("100px");
					h.getChildren().add(hdr);
				}
			}
		} 
		return result;
	}

	private static boolean emptyPropertyName(String aPropertyNames) 
	{
		return aPropertyNames == null || aPropertyNames.trim().length()  == 0 || "*".equals(aPropertyNames);
	}
	
	@SuppressWarnings("unchecked")
	private Listitem makeListitem(Object aItem, List<Method> aGetters)  
	{
		Listitem result = new Listitem();
		
		for (Method m : aGetters) {
			if (m != null) {
				try {
					Object val = m.invoke(aItem, new Object[] {});
					String s = tu.object2String(val);
					Listcell cell = new Listcell(s);
					result.getChildren().add(cell);
					result.setAttribute(ITEM_KEY, aItem);
				} catch (Exception e) {
					e.printStackTrace();//TODO
				} 
			}
		}
		
		return result;
	}
	
	private List<Method> makeGetters(Object aItem, String aPropNames) throws Exception 
	{
		List<Method> result = new ArrayList<Method>();
		
		if (emptyPropertyName(aPropNames)) {
			Method m = aItem.getClass().getMethod("toString", new Class[] {});
			result.add(m);
		} else {
			String props[] = StringUtils.split(aPropNames, ',');
			for (String propname : props) {
				Method m = MethodHelper.getGetter(aItem, propname);
				result.add(m);
			}
		}
		
		return result;
	}
	
	private static String getFirstStringProperty(Collection<?> aItems) 
	{
		if (aItems != null && aItems.size() > 0) {
			return getFirstStringProperty(aItems.iterator().next());
		} else {
			return null;
		}
	}
	
	/**
	 * This method try to find the first String property that is not null
	 * @param aItem
	 * @return
	 */
	private static String getFirstStringProperty (Object aItem) 
	{
		String result = null;
		
		List<MethodCacheData> list = MethodHelper.getMethodDataList(aItem);
		for (MethodCacheData mdata : list) {
			if (mdata.getDtype().getSimpleName().equals("String")) {
				try {
					Object obj = mdata.getMethod().invoke(aItem, new Object[] {});
					if (obj != null) {
						result = mdata.getProperty();
						break;
					}
				} catch (Exception e) {
				}
			}
		}
		return result;
	}
	
	
	public static String valuesToString(Object aItem, String aPropnames) throws Exception 
	{
		List<Method> getters = new ArrayList<Method>();
		
		String propnames = aPropnames;
		if (aPropnames.equals(HDR_FIRST_STRING)) {
			propnames = getFirstStringProperty(aItem);
		} 

		if (emptyPropertyName(propnames)) {
			Method m = aItem.getClass().getMethod("toString", new Class[] {});
			getters.add(m);
		} else {
			String props[] = StringUtils.split(propnames, ',');
			for (String propname : props) {
				Method m = MethodHelper.getGetter(aItem, propname);
				getters.add(m);
			}
		}
		
		String result = "";

		for (Method method : getters) 
			if (method != null) {
				Object o = method.invoke(aItem, new Object[] {});
				
				String s = tu.object2String(o);
				if (s != null) {
					result += s + ",";
				}
		}
		
		return result;
	}
}
